home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
listings
/
v_10_08
/
qcreg.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-11
|
20KB
|
645 lines
///////////////////////////////////////////////////////
// Listing 2 - QCREG.CPP: Quadcode region support
// Written by:
// Kenneth Van Camp
// RR #1 Box 1255
// East Stroudsburg, PA 18301
// (717)223-8620
//
// Functions -
// Region::Region construct from outline
// Region::ScanOutAET create qc's in row
// Region::AddRow add a row of qc's
// Region::AddQC add a single qc
// Region::~Region destructor
// Region::Compress compress the region
// Region::InRegion is qc within region?
// Region::MaxQuits find max #quits
// Region::NumQC find # qc's in region
// operator<< region "put to" stream
// BuildGET build global edge table
// MoveJSortedToAET move row GET to AET
// JSortAET sort AET by column
// AdvanceAET advance edges in AET
// SetRegionYieldFunc set appl. yield func
// DefaultRegionYieldFunc the default yield func
//
///////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#ifdef __TURBOC__
#include <iostream.h>
#else
#include <stream.hpp>
#endif
#include "qc.h"
#define SWAP(a,b) { temp = a; a = b; b = temp; }
// Following class and globals are used locally,
// for construction of regions:
// struct EdgeState: Holds data on edges of one row of
// the polygon (region) during construction.
struct EdgeState
{
struct EdgeState *next_edge;
COORD j,
start_i;
int whole_pixel_jmove,
jdirection;
COORD error_term,
error_term_adj_up,
error_term_adj_down,
count;
};
static EdgeState *GETptr, // Global edge table
*AETptr; // Active edge table
// This sets up the yield function, to allow yielding
// to the user or another application:
int DefaultRegionYieldFunc (int pct_complete);
static int (*RegionYieldFunc) (int) = DefaultRegionYieldFunc;
// Local function prototypes:
static void BuildGET (PointListHeader &vertex_list,
EdgeState *next_free_edge, COORD &min_i, COORD &max_i);
static void MoveJSortedToAET (COORD i_to_move);
static void JSortAET (void);
static void AdvanceAET (void);
///////////////////////////////////////////////////////
// Region::Region: Constructor for region given a
// perimeter list. The following code is based on a
// complex polygon fill routine by Michael Abrash, as
// published in his Graphics Programming column in
// Dr. Dobb's Journal, June 1991, pp. 139-143, 154-157.
// This function will periodically yield control to the
// designated yield function. If the yield function
// returns TRUE, then region building is aborted and a
// flag is set so that subsequent calls to NumQC() will
// return a 0.
Region::Region (PointListHeader &vertex_list)
// vertex_list is an array of points describing the
// outline of the region. The path is
// automatically closed from the last to
// the first point.
{
first_qcnode = NULL;
aborted = FALSE;
ndiv = vertex_list.ndiv;
int nquits = MaxQuits();
assert (nquits > 0);
// Reject polygons that are invisible.
assert (vertex_list.length >= 3);
// Get enough memory to store entire edge table:
EdgeState *edge_table_buf;
edge_table_buf = new EdgeState[vertex_list.length];
assert (edge_table_buf != NULL);
// Build the global edge table:
BuildGET (vertex_list, edge_table_buf, min_i, max_i);
// Scan down through polygon edges, one scan line at
// a time, so long as at least one edge remains in
// either the GET or AET.
// Initialize the active edge table to empty:
AETptr = NULL;
// Start at the top polygon vertex.
current_i = GETptr->start_i;
int nqc_compress = 0; // qc's added since compression
while ((GETptr != NULL) || (AETptr != NULL))
{
// Update AET for this scan line.
MoveJSortedToAET (current_i);
// Add quadcodes for this scan line.
ScanOutAET (current_i, nqc_compress, nquits);
// Advance AET edges one scan line.
AdvanceAET();
// Re-sort on J.
JSortAET();
// Advance to next scan line.
current_i--;
// Single compression pass after 100 quadcodes
if (nqc_compress > 100)
{
nqc_compress = 0;
(void)Compress();
}
// Yield to the user or another application after each
// scan line:
if (RegionYieldFunc (PctComplete()))
{
// The process has been aborted.
// Set a flag, release dynamic memory, and return.
aborted = TRUE;
delete edge_table_buf;
return;
}
}
// Release dynamic memory
delete edge_table_buf;
// Final compression, until nothing more to compress:
while (Compress())
;
} // Region::Region
///////////////////////////////////////////////////////
// Region::ScanOutAET: Create quadcodes along an entire
// row, using the odd/even fill rule.
void Region::ScanOutAET
(COORD i_to_scan, int &nqc_compress, int nquits)
// i_to_scan is the row # to scan
// nqc_compress is the # qc's added since last compress
// nquits is the # of quits to use in each qc
{
// Scan through AET, filling areas along current row
// as each pair of edge crossings is encountered.
// Nearest pixel on or to the right of left edges is
// drawn, and nearest pixel to the left of but not on
// the right edges is drawn.
EdgeState *current_edge = AETptr;
while (current_edge != NULL)
{
COORD left_j = current_edge->j;
current_edge = current_edge->next_edge;
AddRow (i_to_scan, left_j, current_edge->j - 1,
nquits);
nqc_compress += abs (current_edge->j - left_j);
current_edge = current_edge->next_edge;
}
} // Region::ScanOutAET
///////////////////////////////////////////////////////
// Region::AddRow: Add an entire row of quadcodes.
void Region::AddRow
(COORD i, COORD j1, COORD j2, int nquits)
// i is the I coordinate of the row
// j1 is the J coordinate at one end of the row
// j2 is the J coordinate at other end of the row
// nquits is the # quits in the quadcode to add
{
COORD j;
COORD jmin = min (j1, j2);
COORD jmax = max (j1, j2);
for (j = jmax; j >= jmin; j--)
AddQC (i, j, nquits);
} // Region::AddRow
///////////////////////////////////////////////////////
// Region::AddQC: Add a single quadcode to a region.
void Region::AddQC (COORD i, COORD j, int nquits)
// i is the I coordinate of the quadcode
// j is the J coordinate of the quadcode
// nquits is the # quits in the quadcode to add
{
QCNode *new_qcn = new QCNode (i, j, nquits);
assert (new_qcn != NULL);
// Insert into linked list in sorted order.
QCNode *qcn,
*prev = NULL;
for (qcn = first_qcnode; qcn; qcn = qcn->next)
{
if (*new_qcn <= *qcn)
{
// Found insertion point
if (*new_qcn == *qcn)
{
// Quadcode already in list - don't add again.
delete new_qcn;
return;
}
break;
}
prev = qcn;
} // for qcn
new_qcn->next = qcn;
if (prev)
prev->next = new_qcn;
else
// Add to head of list
first_qcnode = new_qcn;
} // Region::AddQC
///////////////////////////////////////////////////////
// Region::~Region: Region destructor
Region::~Region (void)
{
QCNode *qcn,
*nextqcn;
// Release the linked list of quadcode nodes
for (qcn = first_qcnode; qcn; qcn = nextqcn)
{
nextqcn = qcn->next;
delete qcn;
}
first_qcnode = NULL;
} // Region::~Region
///////////////////////////////////////////////////////
// Region::Compress: This function makes a single pass
// at compressing a region, by finding any four consec-
// utive quadcodes that are siblings and replacing them
// with their parent. Return TRUE if any compression
// took p